package org.asiainfo.flowable.service.impl;

import cn.hutool.core.io.IoUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.asiainfo.common.flowable.core.domain.ProcessQuery;
import org.asiainfo.common.flowable.factory.FlowServiceFactory;
import org.asiainfo.common.flowable.utils.ProcessUtils;
import org.asiainfo.common.mybatis.stream.core.page.PageQuery;
import org.asiainfo.common.mybatis.stream.core.page.TableDataInfo;
import org.asiainfo.flowable.domain.WfDeployForm;
import org.asiainfo.flowable.domain.vo.WfDeployVo;
import org.asiainfo.flowable.mapper.WfDeployFormMapper;
import org.asiainfo.flowable.service.WfDeployService;
import org.flowable.common.engine.impl.db.SuspensionState;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * 流程部署服务实现类
 *
 * @author dotor-ww
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class WfDeployServiceImpl extends FlowServiceFactory implements WfDeployService {

    private final WfDeployFormMapper wfDeployFormMapper;

    /**
     * 查询流程部署列表
     *
     * @param processQuery 流程查询实体对象
     * @param pageQuery    分页
     * @return 流程部署列表
     */
    @Override
    public TableDataInfo<WfDeployVo> queryPageList(ProcessQuery processQuery, PageQuery pageQuery) {
        // 1.流程定义列表查询
        ProcessDefinitionQuery processDefinitionQuery = repositoryService
            .createProcessDefinitionQuery()
            .latestVersion()
            .orderByProcessDefinitionKey()
            .asc();
        // 2.构建查询条件
        ProcessUtils.buildProcessSearch(processDefinitionQuery, processQuery);
        long pageTotal = processDefinitionQuery.count();
        if (pageTotal <= 0) {
            return TableDataInfo.build();
        }
        int offSet = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
        // 3.获取流程定义列表
        List<ProcessDefinition> definitions = processDefinitionQuery.listPage(offSet, pageQuery.getPageSize());
        List<WfDeployVo> deployVos = new ArrayList<>(definitions.size());
        for (ProcessDefinition processDefinition : definitions) {
            String deploymentId = processDefinition.getDeploymentId();
            // 4.查询部署流程
            Deployment deployment = repositoryService
                .createDeploymentQuery()
                .deploymentId(deploymentId)
                .singleResult();

            WfDeployVo wfDeployVo = new WfDeployVo();
            wfDeployVo.setDeploymentId(deploymentId);
            wfDeployVo.setDefinitionId(processDefinition.getId());
            wfDeployVo.setProcessKey(processDefinition.getKey());
            wfDeployVo.setProcessName(processDefinition.getName());
            wfDeployVo.setVersion(processDefinition.getVersion());
            wfDeployVo.setCategory(deployment.getCategory());
            wfDeployVo.setSuspended(processDefinition.isSuspended());
            wfDeployVo.setDeploymentTime(deployment.getDeploymentTime());
            wfDeployVo.setCategory(deployment.getCategory());
            deployVos.add(wfDeployVo);
        }
        Page<WfDeployVo> page = new Page<>(deployVos, pageQuery.getPageNum(), pageQuery.getPageSize(), pageTotal);
        return TableDataInfo.build(page);
    }

    /**
     * 查询流程部署版本列表
     *
     * @param processKey 流程定义key
     * @param pageQuery  分页
     * @return 流程部署版本列表
     */
    @Override
    public TableDataInfo<WfDeployVo> publishList(String processKey, PageQuery pageQuery) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService
            .createProcessDefinitionQuery()
            .processDefinitionKey(processKey)
            .orderByProcessDefinitionVersion()
            .desc();

        long pageTotal = processDefinitionQuery.count();
        if (pageTotal <= 0) {
            return TableDataInfo.build();
        }
        int offSet = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
        // 3.获取流程定义列表
        List<ProcessDefinition> definitions = processDefinitionQuery.listPage(offSet, pageQuery.getPageSize());
        List<WfDeployVo> deployVos = new ArrayList<>(definitions.size());
        for (ProcessDefinition processDefinition : definitions) {
            WfDeployVo wfDeployVo = new WfDeployVo();
            wfDeployVo.setDeploymentId(processDefinition.getDeploymentId());
            wfDeployVo.setDefinitionId(processDefinition.getId());
            wfDeployVo.setProcessKey(processDefinition.getKey());
            wfDeployVo.setProcessName(processDefinition.getName());
            wfDeployVo.setVersion(processDefinition.getVersion());
            wfDeployVo.setCategory(processDefinition.getCategory());
            wfDeployVo.setSuspended(processDefinition.isSuspended());
            deployVos.add(wfDeployVo);
        }
        Page<WfDeployVo> page = new Page<>(deployVos, pageQuery.getPageNum(), pageQuery.getPageSize(), pageTotal);
        return TableDataInfo.build(page);
    }

    /**
     * 获取流程定义XML
     *
     * @param definitionId 流程定义ID
     * @return 流程定义XML
     */
    @Override
    public String getBpmnXml(String definitionId) {
        InputStream inputStream = repositoryService.getProcessModel(definitionId);
        return IoUtil.readUtf8(inputStream);
    }

    /**
     * 删除流程部署
     *
     * @param deployIds 流程部署ID集合
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteByDeployIds(Collection<String> deployIds) {
        for (String deployId : deployIds) {
            repositoryService.deleteDeployment(deployId, true);
            wfDeployFormMapper.deleteByQuery(new QueryWrapper().eq(WfDeployForm::getDeployId, deployId));
        }
    }

    /**
     * 激活/挂起流程
     *
     * @param state        状态(1:激活 2:挂起)
     * @param definitionId 流程定义ID
     */
    @Override
    public void changeDeployState(Integer state, String definitionId) {
        // 1.激活流程
        if (SuspensionState.ACTIVE.getStateCode() == state) {
            repositoryService.activateProcessDefinitionById(definitionId, true, null);
        }
        // 2.挂起流程
        if (SuspensionState.SUSPENDED.getStateCode() == state) {
            repositoryService.suspendProcessDefinitionById(definitionId, true, null);
        }
    }
}
